package com.cooper.resource.download;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.net.HttpURLConnection;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.cooper.scraper.HttpDownload;

import android.util.Log;

public abstract class DownloadResource implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -2201706625464233456L;

	private Date createTime;

	private List<DownloadItem> downloadItems = null;

	public DownloadResource() {
		super();
		createTime = new Date();
	}

	public List<DownloadItem> getDownloadItems() {
		return downloadItems;
	}

	public abstract String getThumbUrl();

	public abstract File getDownloadingSaveDir();

	public abstract String getDownloadTitle();

	public abstract List<DownloadItem> analyzeDownloadItem(
			HttpDownload httpDownload) throws PrepareException;

	public Date getAddTime() {
		return createTime;
	}

	public boolean isPrepared() {
		if ((downloadItems == null) || (!getDownloadingSaveDir().exists())) {
			return false;
		} else if (isFinish()) {
			return true;
		} else {
			for (DownloadItem downloadItem : downloadItems) {
				if (!downloadItem.isFinish()
						&& (downloadItem.getFileSize() < 0 || (downloadItem
								.isSupportRange() && !downloadItem
								.getTargetFilePath().exists()))) {
					return false;
				}
			}
		}
		return true;
	}

	public boolean isFinish() {
		if ((downloadItems == null) || downloadItems.isEmpty()) {
			return false;
		}
		for (DownloadItem downloadItem : getDownloadItems()) {
			if (!downloadItem.isFinish()) {
				return false;
			}
		}
		return true;
	}

	public long getTotalSize() {
		long result = 0;
		if (downloadItems == null)
			return -1;
		for (DownloadItem downloadItem : downloadItems) {
			if (downloadItem.getFileSize() < 0) {
				return -1;
			} else {
				result += downloadItem.getFileSize();
			}
		}
		return result;
	}

	public long getDownLen() {
		long result = 0;
		for (DownloadItem downloadItem : downloadItems) {
			result += downloadItem.getDownLength();
		}
		return result;
	}

	// ------prepare

	public void prepare(HttpDownload httpDownload) throws PrepareException {

		if (isPrepared()) {
			// assert is same file
			List<DownloadItem> newDownItems = analyzeDownloadItem(httpDownload);
			for (int i = 0; i < downloadItems.size(); i++) {
				DownloadItem oldDownItem = downloadItems.get(i);
				if (!Thread.currentThread().isInterrupted()
						&& !oldDownItem.isFinish()) {
					oldDownItem.setUrl(newDownItems.get(i).getUrl());
				}
			}
		} else {
			try {
				downloadItems = analyzeDownloadItem(httpDownload);
			} catch (Exception e) {
				throw new PrepareException(true);
			}
			// if downloadItem file change downloadResource.setDownloadItems
			// impl muilt thread down
			File saveDir = getDownloadingSaveDir();
			if (!saveDir.exists()) {
				if (!saveDir.mkdirs()) {
					throw new PrepareException();
				}
			}

			for (DownloadItem downloadItem : downloadItems) {
				if (!Thread.currentThread().isInterrupted()
						&& !downloadItem.isFinish()) {
					prepareItem(httpDownload, downloadItem);
				}
			}
		}
	}

	private static int PREPARE_RETRY_COUNT = 4;

	private static final String NOT_SUPPED_RANGE_EXCEPTION_MSG = "Received authentication challenge is null";

	private void prepareItem(HttpDownload httpDownload,
			DownloadItem downloadItem) throws PrepareException {
		boolean success = false;
		int retryCount = 0;
		while (!Thread.currentThread().isInterrupted() && !success
				&& retryCount <= PREPARE_RETRY_COUNT) {
			try {
				HttpURLConnection conn = httpDownload
						.createDefaultConn(downloadItem.getUrl());
				try {
					if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
						long fileSize = conn.getContentLength();
						if (downloadItem.getFileSize() == fileSize) {
							if (!downloadItem.isSupportRange()) {
								downloadItem.setDownLength(0);
							}
							return;
						}
						downloadItem.setDownLength(0);
						downloadItem.setFileSize(conn.getContentLength());
						downloadItem.setTargetFileExtName(getRemoteFileExtName(
								conn, downloadItem.getUrl()));
						Log.d("DownloadStatus",
								downloadItem.getTargetFileName() + "|len:"
										+ downloadItem.getFileSize());
						success = true;
					} else if (conn.getResponseCode() == HttpURLConnection.HTTP_MOVED_TEMP) {
						downloadItem.setUrl(conn.getHeaderField("Location"));
					} else {
						retryCount++;
						Log.d("DownloadResourceThread", "conn fail code:"
								+ conn.getResponseCode());
					}
				} finally {
					conn.disconnect();
				}
				if (success) {
					conn = httpDownload
							.createDefaultConn(downloadItem.getUrl());
					try {
						conn.setRequestProperty("Range", "bytes=0-");
						try {
							conn.connect();
							Log.d("DownloadStatus",
									"test Range code:" + conn.getResponseCode());
							downloadItem
									.setSupportRange(conn.getResponseCode() == HttpURLConnection.HTTP_OK
											|| conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL);
						} catch (IOException e) {
							if (NOT_SUPPED_RANGE_EXCEPTION_MSG.equals(e
									.getMessage())) {
								downloadItem.setSupportRange(false);
							} else
								throw e;
						}
						if (downloadItem.isSupportRange())
							createRandomAccessFile(downloadItem);
					} finally {
						conn.disconnect();
					}
				}
			} catch (Exception e) {
				success = false;
				retryCount++;
			}
		}
		if (!Thread.currentThread().isInterrupted() && !success) {
			throw new PrepareException();
		}
	}

	private void createRandomAccessFile(DownloadItem downloadItem)
			throws IOException {
		File saveFile = downloadItem.getTargetFilePath();
		if (saveFile.exists()
				&& (saveFile.length() < downloadItem.getFileSize())) {
			saveFile.delete();
		}
		if (!saveFile.exists()) {
			downloadItem.setDownLength(0);
			RandomAccessFile randOut = new RandomAccessFile(saveFile, "rw");
			try {
				randOut.setLength(downloadItem.getFileSize());
			} finally {
				randOut.close();
			}
		}
	}

	private String getRemoteFileExtName(HttpURLConnection conn, String url) {

		String filename = url.substring(url.lastIndexOf('/') + 1);
		if (filename == null || "".equals(filename.trim())) {// 如果获取不到文件名称
			for (int i = 0;; i++) {
				String mine = conn.getHeaderField(i);
				if (mine == null)
					break;
				if ("content-disposition".equals(conn.getHeaderFieldKey(i)
						.toLowerCase())) {
					Matcher m = Pattern.compile(".*filename=(.*)").matcher(
							mine.toLowerCase());
					if (m.find())
						filename = m.group(1);
				}
			}
			// filename = UUID.randomUUID() + ".tmp";// 默认取一个文件名
		}
		if (filename != null && !"".equals(filename.trim())) {
			String result = filename.substring(filename.lastIndexOf('.') + 1);

			if (result != null && !"".equals(result.trim())) {

				if (result.indexOf('?') > 0) {
					result = result.substring(0, result.indexOf('?'));
				}
				return result;
			}
		}
		return DEFAULT_EXT_NAME;
	}

	private static final String DEFAULT_EXT_NAME = "flv";

	public class DownloadItem implements Serializable {

		/**
		 * 
		 */
		private static final long serialVersionUID = -6981754588878960691L;

		private String targetFileName;

		private String url;

		private String targetFileExtName = null;

		private long fileSize = -1;

		private long downLength = 0;

		private long pos = 0;

		// private long endPos

		private boolean supportRange = true;

		private boolean finish = false;

		public boolean isSupportRange() {
			return supportRange;
		}

		public void setSupportRange(boolean supportRange) {
			this.supportRange = supportRange;
		}

		public DownloadItem() {
			super();
		}

		public DownloadItem(String targetFileName, String url) {
			super();
			this.targetFileName = targetFileName;
			this.url = url;
		}

		public File getTargetFilePath() {
			return new File(getDownloadingSaveDir().getAbsolutePath() + "/"
					+ getTargetFileName() + "." + getTargetFileExtName());
		}

		public String getTargetFileName() {
			return targetFileName;
		}

		public void setTargetFileName(String targetFileName) {
			this.targetFileName = targetFileName;
		}

		public String getUrl() {
			return url;
		}

		public void setUrl(String url) {
			this.url = url;
		}

		public long getFileSize() {
			return fileSize;
		}

		public void setFileSize(long fileSize) {
			this.fileSize = fileSize;
		}

		public String getTargetFileExtName() {
			return targetFileExtName;
		}

		public void setTargetFileExtName(String targetFileExtName) {
			this.targetFileExtName = targetFileExtName;
		}

		public long getDownLength() {
			return downLength;
		}

		public void setDownLength(long downLength) {
			this.downLength = downLength;
		}

		public long getPos() {
			return pos;
		}

		public void setPos(long pos) {
			this.pos = pos;
		}

		public long getStartPos() {
			return pos + downLength;
		}

		public boolean isFinish() {
			return finish;
		}

		public void setFinish(boolean finish) {
			this.finish = finish;
		}

	}

	public class PrepareException extends IOException {

		/**
		 * 
		 */
		private static final long serialVersionUID = -3365955255425248699L;

		private boolean analyze;

		public PrepareException(boolean analyze) {
			super();
			this.analyze = analyze;
		}

		public PrepareException() {
			super();
			this.analyze = false;
		}

		public boolean isAnalyze() {
			return analyze;
		}

	}
}
